using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Design;
using System.Drawing.Drawing2D;

namespace DMTT.Workflow.Activities.Design
{
    [DefaultProperty("ColorSelection")]
    [DefaultEvent("BeforeLinkCreation")]
    [System.ComponentModel.DesignerCategory("Code")]
	public class TreeViewConnectionPanel : Panel
	{
		
		#region Private Variables
		
		private ConnectionDrawingService m_drawingService;
		private bool m_dragExit = false;
        private bool m_allowMultiSelection = true;
		
		#endregion


        #region Constructors

		public TreeViewConnectionPanel()
		{
			this.SetStyle(
                ControlStyles.DoubleBuffer |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.ResizeRedraw |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.UserPaint, true);
			
			this.InitializeComponent();
			
			m_drawingService = new ConnectionDrawingService();
            m_drawingService.TreeViewLinkDeleted += delegate(object sender, TreeViewLinkDeletedEventArgs e)
            {
                this.OnTreeViewLinkDeleted(e);
            };
        }

        #endregion
        

        #region Componentes

        private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
        private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1;
		private System.ComponentModel.IContainer components;
		
		#endregion
		

		#region Overrides
        
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            
            Graphics gr = this.CreateGraphics();
            gr.SmoothingMode = SmoothingMode.AntiAlias;
            
            if (m_drawingService.HitTestTreeView(gr, e.Location) == false)
            {
                this.SelectedItem = null;
            }
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            base.OnPaint(e);
            m_drawingService.RedrawItems(e.Graphics);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            
            Graphics gr = this.CreateGraphics();
            gr.SmoothingMode = SmoothingMode.AntiAlias;

            LinkItem item = m_drawingService.GetTreeViewItemAt(gr, new Point(e.X, e.Y));
            if (item != null)
            {
                m_drawingService.SelectTreeViewItemAt(gr, new Point(e.X, e.Y));
                if (item is TreeViewLinkItem)
                {
                    TreeViewLinkItem linkItem = item as TreeViewLinkItem;
                    linkItem.SourceNode.TreeView.SelectedNode = linkItem.SourceNode;
                    linkItem.DestinationNode.TreeView.SelectedNode = linkItem.DestinationNode;
                }
                this.Invalidate();
                if (e.Button == MouseButtons.Right && this.m_drawingService.SelectedTreeViewItem != null)
                    this.contextMenuStrip1.Show(this, new Point(e.X, e.Y));
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (m_dragExit)
            {
                m_dragExit = false;
                m_drawingService.DestroyDragLink();
                this.Invalidate();
            }
        }

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            base.OnDragOver(drgevent);
            if (drgevent.Data.GetDataPresent(typeof(TreeView)) || drgevent.Data.GetDataPresent(typeof(TreeViewEx)))
            {
                TreeView sourceTree = drgevent.Data.GetData(typeof(TreeView)) as TreeView;
                if (sourceTree == null)
                {
                    sourceTree = drgevent.Data.GetData(typeof(TreeViewEx)) as TreeViewEx;
                }
                if (sourceTree == null)
                {
                    drgevent.Effect = DragDropEffects.None;
                    return;
                }
                Point startPos;
                Point endPos;
                Point sourcePos = m_drawingService.GetTreeNodePos(sourceTree, false);
                Point cursorPos = this.PointToClient(new Point(drgevent.X, drgevent.Y));

                if (sourcePos.X < cursorPos.X)
                {
                    sourcePos.X += sourceTree.Width;
                    startPos = sourcePos;
                    endPos = cursorPos;
                }
                else
                {
                    startPos = cursorPos;
                    endPos = sourcePos;
                }

                this.m_drawingService.CreateDragLink(
                    startPos,
                    endPos);
                this.Invalidate();
                drgevent.Effect = DragDropEffects.Link;
            }
            else
            {
                drgevent.Effect = DragDropEffects.None;
            }
        }

        protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
        {
            base.OnGiveFeedback(gfbevent);
            m_drawingService.DestroyDragLink();
        }
		
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

        protected override void OnResize(EventArgs eventargs)
        {
            base.OnResize(eventargs);
            this.RefreshDisplay();
        }
		
		protected override void OnControlAdded(ControlEventArgs e)
		{
			base.OnControlAdded(e);

            if (e.Control is TreeView || e.Control.GetType().BaseType == typeof(TreeView))
            {
                TreeView tree = e.Control as TreeView;

                tree.DragOver += new System.Windows.Forms.DragEventHandler(this.TreeView_DragOver);
                tree.DragDrop += new System.Windows.Forms.DragEventHandler(this.TreeView_DragDrop);
                tree.DragEnter += new System.Windows.Forms.DragEventHandler(this.TreeView_DragEnter);
                tree.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.TreeView_ItemDrag);
                tree.GiveFeedback += new System.Windows.Forms.GiveFeedbackEventHandler(this.TreeView_GiveFeedback);
                tree.MouseDown += new MouseEventHandler(this.TreeView_MouseDown);
                tree.BeforeCollapse += new TreeViewCancelEventHandler(this.TreeView_BeforeCollapse);
                tree.NodeMouseClick += new TreeNodeMouseClickEventHandler(this.TreeView_NodeMouseClick);

                if (e.Control is TreeViewEx)
                {
                    ((TreeViewEx)e.Control).LinkItems = this.LinkItems;
                    ((TreeViewEx)e.Control).Scroll += new TreeViewEx.ScrollEventHandler(this.TreeView_Scroll);
                }
            }
        }
		
		#endregion


        #region Public Properties

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ConnectionDrawingService ConnectionDrawingService
		{
			get{return m_drawingService;}
		}
        
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TreeViewLinkItemCollection LinkItems
        {
            get { return m_drawingService.TreeViewLinkItems; }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TreeViewLinkItem SelectedItem
        {
            get { return m_drawingService.SelectedTreeViewItem; }
            set { m_drawingService.SelectedTreeViewItem = value; }
        }

        [RefreshProperties(RefreshProperties.Repaint)]
        [Category("Layout")]
        [DefaultValue(typeof(Color), "White")]
        public Color ColorTop
        {
            get { return m_drawingService.ColorTop; }
            set { m_drawingService.ColorTop = value; this.Invalidate(); }
        }

        [RefreshProperties(RefreshProperties.Repaint)]
        [Category("Layout")]
        [DefaultValue(typeof(Color), "LightGray")]
        public Color ColorInner
        {
            get { return m_drawingService.ColorInner; }
            set { m_drawingService.ColorInner = value; this.Invalidate(); }
        }

        [RefreshProperties(RefreshProperties.Repaint)]
        [Category("Layout")]
        [DefaultValue(typeof(Color), "Black")]
        public Color ColorBottom
        {
            get { return m_drawingService.ColorBottom; }
            set { m_drawingService.ColorBottom = value; this.Invalidate(); }
        }

        [RefreshProperties(RefreshProperties.Repaint)]
        [Category("Layout")]
        [DefaultValue(typeof(Color), "DarkBlue")]
        public Color ColorSelection
        {
            get { return m_drawingService.ColorSelection; }
            set { m_drawingService.ColorSelection = value; this.Invalidate(); }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        public bool AllowMultiSelection
        {
            get { return m_allowMultiSelection; }
            set { m_allowMultiSelection = value; }
        }
		
		#endregion


        #region Public Events

        #region BeforeTreeViewLinkCreation

        public event BeforeTreeViewLinkCreationEventHandler BeforeTreeViewLinkCreation;

        protected virtual bool OnBeforeTreeViewLinkCreation(BeforeTreeViewLinkCreationEventArgs e)
        {
            if (BeforeTreeViewLinkCreation != null)
            {
                BeforeTreeViewLinkCreation(this, e);
                return e.Cancel;
            }
            return false;
        }

        #endregion

        #region TreeViewLinkDeleted

        public event TreeViewLinkDeletedEventHandler TreeViewLinkDeleted;

        protected virtual void OnTreeViewLinkDeleted(TreeViewLinkDeletedEventArgs e)
        {
            this.RefreshDisplay();
            if (TreeViewLinkDeleted != null)
            {
                TreeViewLinkDeleted(this, e);
            }
        }

        #endregion

        #endregion
		
		
		#region Private Functions
		
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();

            this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripMenuItem1.Name = "toolStripMenuItem1";
            this.toolStripMenuItem1.Size = new System.Drawing.Size(152, 22);
            this.toolStripMenuItem1.Text = "&Delete";
            this.toolStripMenuItem1.Image = Resources.DeleteItem.ToBitmap();
            this.toolStripMenuItem1.Click += new System.EventHandler(this.menuItem1_Click);

            this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.toolStripMenuItem1});
		}

        private void RefreshDisplay()
        {
            foreach (TreeViewLinkItem item in this.ConnectionDrawingService.TreeViewLinkItems)
            {
                item.Refresh();
            }
            foreach (Control ctl in this.Controls)
            {
                if (ctl is TreeView || ctl is TreeViewEx)
                {
                    ctl.Invalidate();
                }
            }
            this.Invalidate();
        }

        private void ResetTreeViewSelection(TreeView view)
        {
            foreach (Control ctl in this.Controls)
            {
                if ((ctl is TreeView || ctl is TreeViewEx) && ctl != view)
                {
                    ((TreeView)ctl).SelectedNode = null;
                }
            }
        }

        private bool IsLinked(TreeNode node)
        {
            foreach (TreeViewLinkItem item in this.LinkItems)
            {
                if (item.SourceNode == node || item.DestinationNode == node)
                {
                    return true;
                }
            }
            return false;
        }

        private LinkItem GetNodeFromLinkItem(TreeNode node)
        {
            foreach (TreeViewLinkItem item in this.LinkItems)
            {
                if (item.SourceNode == node || item.DestinationNode == node)
                {
                    return item;
                }
            }
            return null;
        }
		
		#endregion
		
		
		#region Event Handling

        #region TreeView

        private void TreeView_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
        {
            bool drag = true;
            if (m_allowMultiSelection == false && this.IsLinked((TreeNode)e.Item))
            {
                drag = false;
            }

            if (drag)
            {
                this.SelectedItem = null;
                this.ResetTreeViewSelection((TreeView)sender);
                ((TreeView)sender).DoDragDrop(sender, DragDropEffects.Link);
            }
        }

        private void TreeView_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(TreeView)) || e.Data.GetDataPresent(typeof(TreeViewEx)))
            {
                TreeView sourceTree = e.Data.GetData(typeof(TreeView)) as TreeView;
                if (sourceTree == null)
                {
                    sourceTree = e.Data.GetData(typeof(TreeViewEx)) as TreeViewEx;
                }

                TreeView destTree = sender as TreeView;
                if (sourceTree == null || destTree == null)
                {
                    e.Effect = DragDropEffects.None;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }
                else if (sourceTree == destTree)
                {
                    e.Effect = DragDropEffects.Link;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }

                destTree.Select();

                Point startPos;
                Point endPos;
                Point cursorPos;
                Point sourcePos = m_drawingService.GetTreeNodePos(sourceTree, false);
                Point itemPoint = destTree.PointToClient(new Point(e.X, e.Y));

                TreeNode node = destTree.GetNodeAt(itemPoint.X, itemPoint.Y);
                if (node != null)
                {
                    destTree.SelectedNode = node;
                    cursorPos = m_drawingService.GetTreeNodePos(destTree, false);
                    if (cursorPos.X < sourcePos.X)
                        cursorPos.X += destTree.Width;
                }
                else
                {
                    cursorPos = this.PointToClient(new Point(e.X, e.Y));
                }

                if (m_allowMultiSelection == false && this.IsLinked(node))
                {
                    e.Effect = DragDropEffects.None;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }

                if (this.OnBeforeTreeViewLinkCreation(new BeforeTreeViewLinkCreationEventArgs(sourceTree.SelectedNode, node)))
                {
                    e.Effect = DragDropEffects.None;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }

                e.Effect = DragDropEffects.Link;

                if (sourcePos.X < cursorPos.X)
                {
                    sourcePos.X += sourceTree.Width;
                    if (cursorPos.X > destTree.Bounds.Left)
                        cursorPos.X = destTree.Bounds.Left;
                    startPos = sourcePos;
                    endPos = cursorPos;
                }
                else
                {
                    if (cursorPos.X < destTree.Bounds.Right)
                        cursorPos.X = destTree.Bounds.Right;
                    startPos = cursorPos;
                    endPos = sourcePos;
                }

                this.m_drawingService.CreateDragLink(startPos, endPos);
                this.Invalidate();
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void TreeView_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
        {
            e.Effect = DragDropEffects.Link;
        }

        private void TreeView_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(TreeView)) || e.Data.GetDataPresent(typeof(TreeViewEx)))
            {
                TreeView sourceTree = e.Data.GetData(typeof(TreeView)) as TreeView;
                if (sourceTree == null)
                {
                    sourceTree = e.Data.GetData(typeof(TreeViewEx)) as TreeViewEx;
                }
                TreeView destTree = sender as TreeView;
                if (sourceTree == null || destTree == null || sourceTree == destTree)
                {
                    e.Effect = DragDropEffects.None;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }

                TreeView startTree;
                TreeView endTree;
                Point cursorPos;
                Point sourcePos = m_drawingService.GetTreeNodePos(sourceTree, false);
                Point itemPoint = destTree.PointToClient(new Point(e.X, e.Y));
                
                TreeNode node = destTree.GetNodeAt(itemPoint.X, itemPoint.Y);
                if (node != null)
                {
                    destTree.SelectedNode = node;
                    cursorPos = m_drawingService.GetTreeNodePos(destTree, false);
                    if (cursorPos.X < sourcePos.X)
                        cursorPos.X += destTree.Width;
                }
                else
                {
                    e.Effect = DragDropEffects.None;
                    m_drawingService.DestroyDragLink();
                    this.Invalidate();
                    return;
                }

                if (sourcePos.X < cursorPos.X)
                {
                    startTree = sourceTree;
                    endTree = destTree;
                }
                else
                {
                    startTree = destTree;
                    endTree = sourceTree;
                }

                if (this.OnBeforeTreeViewLinkCreation(new BeforeTreeViewLinkCreationEventArgs(sourceTree.SelectedNode, node)) == false)
                {
                    LinkItem item = m_drawingService.AddLinkItem(startTree, endTree);
                    this.SelectedItem = (TreeViewLinkItem)item;
                }

                startTree.Invalidate();
                endTree.Invalidate();
            }
            m_drawingService.DestroyDragLink();
            this.Invalidate();
        }

        private void TreeView_GiveFeedback(object sender, System.Windows.Forms.GiveFeedbackEventArgs e)
        {
            m_dragExit = true;
        }

        private void TreeView_MouseDown(object sender, MouseEventArgs e)
        {
            TreeNode node = ((TreeView)sender).GetNodeAt(e.Location);
            if (node != null)
            {
                ((TreeView)sender).SelectedNode = node;
            }
        }

        private void TreeView_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            e.Cancel = true;
        }

        private void TreeView_Scroll(object sender, EventArgs e)
        {
            this.RefreshDisplay();
        }

        private void TreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            LinkItem item = this.GetNodeFromLinkItem(e.Node);
            if (item != null)
            {
                this.SelectedItem = (TreeViewLinkItem)item;
            }
            else
            {
                this.SelectedItem = null;
            }
            this.Invalidate();
        }

        #endregion
		
		#region Menu
		
		private void menuItem1_Click(object sender, System.EventArgs e)
		{
            this.m_drawingService.RemoveTreeViewItem(this.m_drawingService.SelectedTreeViewItem);
            
            Graphics gr = this.CreateGraphics();
            gr.SmoothingMode = SmoothingMode.AntiAlias;
            
            m_drawingService.RedrawItems(gr);
			this.Invalidate();
		}
		
		#endregion
		
		#endregion

    }

    #region Delegates

    public delegate void BeforeTreeViewLinkCreationEventHandler(object sender, BeforeTreeViewLinkCreationEventArgs e);
    public delegate void TreeViewLinkDeletedEventHandler(object sender, TreeViewLinkDeletedEventArgs e);

    #endregion

    #region BeforeTreeViewLinkCreationEventArgs

    public class BeforeTreeViewLinkCreationEventArgs : EventArgs
    {
        private TreeNode m_sourceNode;
        private TreeNode m_destinationNode;
        private bool m_cancel;

        public BeforeTreeViewLinkCreationEventArgs(TreeNode sourceNode, TreeNode destinationNode)
        {
            m_sourceNode = sourceNode;
            m_destinationNode = destinationNode;
            m_cancel = false;
        }

        public TreeNode SourceNode
        {
            get { return m_sourceNode; }
        }

        public TreeNode DestinationNode
        {
            get { return m_destinationNode; }
        }

        public bool Cancel
        {
            get { return m_cancel; }
            set { m_cancel = value; }
        }
    }

    #endregion

    #region TreeViewLinkDeletedEventArgs

    public class TreeViewLinkDeletedEventArgs : EventArgs
    {
        private TreeNode m_node;

        public TreeViewLinkDeletedEventArgs(TreeNode node)
        {
            m_node = node;
        }

        public TreeNode Node
        {
            get { return m_node; }
        }
    }

    #endregion

}
